﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WsProtocol.IEC62056.DataTypeClass;
using WsProtocol.IEC62056.CosemACSE;
using Comm.PublicClass;
using Comm.MultiLan;


namespace WsProtocol.IEC62056
{   
    [Serializable]
    public class CosemApdu 
    {        
        private ClientApduSel m_rwflag = ClientApduSel.GETRequest;  //"请求类型"   
        private string m_followreport = string.Empty;   //"跟随上报信息域"       
        private string m_rwtype = string.Empty;          //"读写选择类型"
        private long m_frameseq = 0;           //帧序号
        private bool m_finished = true;    　//末帧标志
        private string m_RawData = string.Empty; //分帧时的当前帧数据
        private List<CosemObject> m_OIList;
        private List<CosemObject> m_FROIList;//　跟随上报信息域的对象列表，只解析数据时存在       
        private Encryption m_encryption;
        private string m_piid = "41";//"81"; //优先级 
        private int m_ApduSize = 128;
        private string m_Conformance = "007E1F";
        #region Public Properties

        public bool NeedResponse {
            set { encryption.NeedResponse = value; }
        }
        public ClientApduSel rwflag
        {
            get
            {
                return m_rwflag;
            }
            set
            {
                m_rwflag = value;
            }
        }         //"请求类型"   
        public string followreport
        {
            get
            {
                return m_followreport;
            }
            set
            {
                m_followreport = value;
            }
        }   //"跟随上报信息域"
       
        public string rwtype
        {
            get
            {
                return m_rwtype;
            }
            set
            {
                m_rwtype = value;
            }
        }          //"读写选择类型"
        public List<CosemObject> OIList
        {
            get
            {
                return m_OIList;
            }
            set
            {
                m_OIList = value;
            }
        }

        public List<CosemObject> FROIList
        {
            get
            {
                return m_FROIList;
            }
            set
            {
                m_FROIList = value;
            }
        }

        public long frameseq
        {
            get
            {
                return m_frameseq;
            }
            set
            {
                m_frameseq = value;
            }
        }

        public bool finished
        {
            get
            {
                return m_finished;
            }
            set
            {
                m_finished = value;
            }
        }

      
        public Encryption encryption
        {
            get
            {
                return m_encryption;
            }
            set
            {
                m_encryption = value;
                Data.encryption = value;
            }
        }
        public string PIID
        {
            get
            {
                return m_piid;
            }
            set
            {
                m_piid = value;
            }
        }
  
        public int ApduSize
        {
            get
            {
                return m_ApduSize;
            }
            set
            {
                if (value != 0)  //如果为零直接用128
                    m_ApduSize = value -3; //14;//直接减掉连路层的14字节和3字节PADU头 E6E600
            }
        }

        public string Conformance
        {
            get
            {
                return m_Conformance;
            }
            set
            {
                m_Conformance = value.Length > 6 ? value.Substring(0, 6) : value.PadLeft(6, '0'); 
            }
        }

        public string RawData
        {
            get
            {
                return m_RawData;
            }
            set
            {
                m_RawData = value;
            }
        }

        public ServerApduSel ResMode{get;set;}
        #endregion

        
        public CosemApdu()
        {
            m_OIList = new List<CosemObject>();
            m_encryption = new Encryption();
            NeedResponse = true;           
        }
      

        //添加OI对象
        public int AddObAttribute(CosemObject mObj)
        {
            if (m_OIList == null)
                m_OIList = new List<CosemObject>();
            m_OIList.Add(mObj);
            return 1;
        }        

        /*
      应用层服务  CHOICE    
      
   */
        public string RequestApdu(ref string errorinfo)
        {
            try
            {
                string FormatValue = string.Empty;
                errorinfo = string.Empty;             
                //ClientApduSel RWMode = (ClientApduSel)Enum.Parse(typeof(ClientApduSel), m_rwflag);
                
                switch (m_rwflag)
                {
                    case ClientApduSel.DLConnectRequest:
                        FormatValue = DLL_Connect.Request(m_OIList[0],ref errorinfo);

                        return FormatValue;
                    case ClientApduSel.ConnectRequest:
                        FormatValue = ACSE_Connect.Request(m_OIList[0], encryption, ref errorinfo);
                        return FormatValue;

                    case ClientApduSel.DLReleaseRequest:
                        FormatValue = DLL_Release.Request();
                        return FormatValue;
                    case ClientApduSel.ReleaseRequest:
                        FormatValue = ACSE_Release.Request(m_OIList[0], encryption, ref errorinfo);
                        return FormatValue;

                    case ClientApduSel.GETRequest:
                        FormatValue = ACSE_Get.Request(m_OIList, m_rwtype, m_frameseq, ref errorinfo);

                        GetRRMode getMode = (GetRRMode)Enum.Parse(typeof(GetRRMode), rwtype);
                        m_rwtype = MyConvert.DecToHex(((int)getMode).ToString(), 2);
                        break;
                    case ClientApduSel.SETRequest:
                    case ClientApduSel.Generalglocipher:
                    case ClientApduSel.Generaldedcipher:
                        FormatValue = ACSE_Set.Request(m_OIList,  m_rwtype, ApduSize, ref errorinfo);

                        SetRRMode setMode = (SetRRMode)Enum.Parse(typeof(SetRRMode), rwtype);
                        m_rwtype = MyConvert.DecToHex(((int)setMode).ToString(), 2);
                        break;
                    case ClientApduSel.ActionRequest:
                        FormatValue = ACSE_Action.Request(m_OIList, m_rwtype, frameseq, finished, ref errorinfo);

                        ActionRRMode ActionMode = (ActionRRMode)Enum.Parse(typeof(ActionRRMode), rwtype);
                        m_rwtype = MyConvert.DecToHex(((int)ActionMode).ToString(), 2);
                        break;                    
                    
                    case ClientApduSel.EventRequest:
                        FormatValue = ACSE_Event.Request(m_OIList[0], ref errorinfo);
                        break;
                }
                int HdlLen = 6; //ClientApduSel+m_rwtype+PIID.Length;
                bool BlockFlag = false;
                int EncryptionIncrement = 0;  //计算加密后数据长度增量
                //if ((encryption.ContextName == RequirContext.LN_Cipher || encryption.apduAuth >= Authentication.HLS)
                //    && encryption.securityPolicy != SecurityPolicy.Nothing)
                if (encryption.IfEncrypt())
                {
                    switch (encryption.securityPolicy)
                    {
                        case SecurityPolicy.需认证: EncryptionIncrement = 21; break;//此长度应该考虑了数据长超127字节的情况，长度字节最长3字节
                        case SecurityPolicy.需加密: EncryptionIncrement = 9; break;
                        case SecurityPolicy.需认证加密: EncryptionIncrement = 21; break;
                    }
                }
                if (FormatValue.Length + EncryptionIncrement * 2 + HdlLen > ApduSize * 2)
                { //需要分块 
                    BlockFlag = true;                 
                }

                if (!BlockFlag)
                    FormatValue = MyConvert.DecToHex(((int)m_rwflag).ToString(), 2) + m_rwtype + m_piid + FormatValue;

                FormatValue = GetEncFrameValue(FormatValue, EncryptionIncrement, BlockFlag, ref errorinfo);

                return FormatValue;//到此整个应用层用户数据组织完毕

            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                return string.Empty;
            }
        }

        private string GetEncFrameValue(string FormatValue, int IncLen, bool BlockFlag, ref string errorinfo)
        {            
            try {

                if (BlockFlag)  //需要分块
                {
                    
                    return GetEncBlockFrame(FormatValue, IncLen, ref errorinfo);
                }
                else
                {                    
                    if (IncLen > 0)
                    {
                        encryption.PlaintData = FormatValue;
                        FormatValue = encryption.GCM_AESEncrypt(ref errorinfo);
                        if (!string.IsNullOrEmpty(errorinfo))
                            return string.Empty;

                        switch (m_rwflag)
                        {
                            case ClientApduSel.SETRequest:
                                m_rwflag = ClientApduSel.gloSetRequest;
                                break;
                            case ClientApduSel.GETRequest:
                                m_rwflag = ClientApduSel.gloGetRequest;
                                break;
                            case ClientApduSel.ActionRequest:
                                m_rwflag = ClientApduSel.gloActionRequest;
                                break;
                            case ClientApduSel.Generalglocipher:
                            case ClientApduSel.Generaldedcipher:
                                FormatValue = GeneralGloCiphering.Request(encryption.GetSysTitle(), FormatValue, ref errorinfo);
                                break;
                        }

                        FormatValue = MyConvert.DecToHex(((int)m_rwflag).ToString(), 2) + octString.GetFormatValue(FormatValue, ref errorinfo);
                    }
                    return FormatValue;
                }                       
            }
            catch (Exception ex) {
                errorinfo = ex.ToString();
                return string.Empty;
            }
            
        }

        private string GetEncBlockFrame(string FormatValue, int IncLen, ref string errorinfo)
        {
            try
            {
                List<string> FrameList = new List<string>();
                string ApduSelTag = MyConvert.DecToHex(((int)m_rwflag).ToString(), 2);
                string mFormatValue = FormatValue.Substring(20);
                int BlockbyteLen = ApduSize - IncLen - 3; ////3是ApduSelTag + ModeTag + m_piid字节长
                List<string> PlaintDataList = new List<string>();
                encryption.PlaintData = "";
                int Seq = 1;
                switch (m_rwflag)
                {
                    case ClientApduSel.SETRequest:
                        SetRRMode setMode = (SetRRMode)Enum.Parse(typeof(SetRRMode), rwtype);                       
                        while (mFormatValue != "") {
                            if (Seq == 1) {
                                if (setMode == SetRRMode.SetList)
                                {
                                    setMode = SetRRMode.SetListFirstDatablock;                                    
                                    FormatValue = SetListFirstOrLastDatablock.Request(OIList, ref  mFormatValue, BlockbyteLen, ref errorinfo);
                                }
                                else
                                {
                                    setMode = SetRRMode.SetFirstDatablock;                                  
                                    FormatValue = SetFirstOrLastDatablock.Request(OIList[0], ref mFormatValue, BlockbyteLen, ref errorinfo);
                                }
                            }
                            else {
                                setMode = SetRRMode.SetDatablock;                              
                                FormatValue = SetDatablock.Request(ref mFormatValue, BlockbyteLen, Seq, ref errorinfo);
                                
                            }
                            FormatValue = ApduSelTag + MyConvert.DecToHex(((int)setMode).ToString(), 2) + m_piid + FormatValue;
                                                      
                            if (IncLen > 0)
                            {
                                PlaintDataList.Add(FormatValue);  
                                encryption.PlaintData = FormatValue;
                                FormatValue = encryption.GCM_AESEncrypt(ref errorinfo);
                                if (!string.IsNullOrEmpty(errorinfo))
                                    return string.Empty;

                                FormatValue = MyConvert.DecToHex(((int)ClientApduSel.gloSetRequest).ToString(), 2) + octString.GetFormatValue(FormatValue, ref errorinfo);
                            }
                            encryption.PlaintData = string.Join(",", PlaintDataList.ToArray());
                            FrameList.Add(FormatValue);
                            Seq++;                        
                        }                    
                        break;
                    case ClientApduSel.ActionRequest:
                        ActionRRMode ActMode = (ActionRRMode)Enum.Parse(typeof(ActionRRMode), rwtype);
                        while (mFormatValue != "")
                        {
                            if (Seq == 1)
                            {
                                if (ActMode == ActionRRMode.ActionList)
                                {
                                    ActMode = ActionRRMode.ActionListFirstPblock;
                                    FormatValue = ActionListFirstPblock.Request(OIList, ref  mFormatValue, BlockbyteLen, ref errorinfo);
                                }
                                else
                                {
                                    ActMode = ActionRRMode.ActionFirstPblock;
                                    FormatValue = ActionFirstPblock.Request(OIList[0], ref mFormatValue, BlockbyteLen, ref errorinfo);
                                }
                            }
                            else
                            {
                                ActMode = ActionRRMode.ActionPblock;
                                FormatValue = ActionPblock.Request(ref mFormatValue, BlockbyteLen, Seq, ref errorinfo);

                            }
                            FormatValue = ApduSelTag + MyConvert.DecToHex(((int)ActMode).ToString(), 2) + m_piid + FormatValue;

                            if (IncLen > 0)
                            {
                                PlaintDataList.Add(FormatValue);
                                encryption.PlaintData = FormatValue;
                                FormatValue = encryption.GCM_AESEncrypt(ref errorinfo);
                                if (!string.IsNullOrEmpty(errorinfo))
                                    return string.Empty;

                                FormatValue = MyConvert.DecToHex(((int)ClientApduSel.gloActionRequest).ToString(), 2) + octString.GetFormatValue(FormatValue, ref errorinfo);
                            }
                            encryption.PlaintData = string.Join(",", PlaintDataList.ToArray());
                            FrameList.Add(FormatValue);
                            Seq++;
                        }                    
                        break;
                    default:
                        errorinfo = clsLan.GetLan("此服务不支持分块");
                        return FormatValue;
                }               

                return string.Join(",", FrameList.ToArray());
            }
            catch (Exception ex) {
                errorinfo = ex.ToString();
                return FormatValue;
            }
        }
        /*应用层服务  CHOICE          
        跟随上报信息域 	FollowReport  OPTIONAL，
        
      */
        public int ResponseApdu(string frameData, ref string errorinfo)
        {
            int iResult = 0;
            try
            {
                if (string.IsNullOrEmpty(frameData)) return iResult;
                encryption.PlaintData = string.Empty;
                int mode = Convert.ToInt32(frameData.Substring(0, 2), 16);
                ServerApduSel ResMode = (ServerApduSel)mode;//把最外层的服务标志保留                 
                rwtype = ResMode.ToString();
                if (mode >= (int)ServerApduSel.gloGetResponse && mode <= (int)ServerApduSel.Generalcipher
                    && mode!=(int)ServerApduSel.exResponse && mode!=(int)ServerApduSel.AccessResponse) //这两个不需进行解密                    
                {
                    int keyFlag = 0; //解密 0用EK密钥,1用DK密钥
                    string FormatValue = string.Empty;
                    string systemtitle = string.Empty;
                    switch (ResMode)
                    {                  
                        
                        case ServerApduSel.Generalglocipher: //把systitle解析出来,剩下的为密文
                            iResult = GeneralGloCiphering.Response(ref frameData,m_OIList[0], ref errorinfo);
                            keyFlag = 0;
                            break;
                        case ServerApduSel.Generalcipher:
                            iResult = GeneralCiphering.Response(ref frameData, m_OIList[0], ref errorinfo);
                            keyFlag = 0;
                            break;
                        case ServerApduSel.gloGetResponse:
                        case ServerApduSel.gloSetResponse:
                        case ServerApduSel.gloActionResponse:                      
                            keyFlag = 0;
                            break;
                        case ServerApduSel.Generaldedcipher:
                            iResult = GeneralDedCiphering.Response(ref frameData, m_OIList[0], ref errorinfo);
                            keyFlag = 1;
                            break;
                        case ServerApduSel.dedGetResponse:
                        case ServerApduSel.dedSetResponse:
                        case ServerApduSel.dedActionResponse:
                            keyFlag = 1;
                            break;                            
                    }
                    frameData = frameData.Substring(2); //去掉glo
                    FormatValue = octString.AnalizeDataSpecial(ref frameData, ref errorinfo);
                  
                    encryption.EncryptData = FormatValue;
                    
                    FormatValue = encryption.GCM_AESDecrypt(keyFlag, ref errorinfo);//解密 用EK密钥
                    if (!string.IsNullOrEmpty(errorinfo))
                        return -1;
                    frameData = FormatValue + frameData; //解密出来的数据再加上原帧数据，可能存在不用加密的附加数据
                    mode = Convert.ToInt32(frameData.Substring(0, 2), 16);
                    ResMode = (ServerApduSel)mode;
                }
              
                if (ResMode.ToString() == mode.ToString())
                {
                    errorinfo = clsLan.GetLan("无效的服务标志");
                    frameData = "";
                    return -1;
        
                }                

                if (ResMode != ServerApduSel.DLConnectResponse)
                    frameData = frameData.Substring(2); //去掉前面的回应标志

             
                if (m_OIList == null || m_OIList.Count == 0)
                {
                    m_OIList.Add(new CosemObject());
                }

                m_RawData = string.Empty;
                m_finished = true;
                m_frameseq = 0;
                switch (ResMode)
                {
                    case ServerApduSel.GETResponse:
                        iResult = ACSE_Get.Response(ref frameData, m_OIList, ref m_frameseq, ref m_finished, ref m_RawData, ref errorinfo);
                        break;
                    case ServerApduSel.SETResponse:
                        iResult = ACSE_Set.Response(ref frameData, m_OIList, ref  m_frameseq, ref errorinfo);
                        break;
                    case ServerApduSel.ActionResponse:
                        iResult = ACSE_Action.Response(ref frameData, m_OIList, ref  m_frameseq, ref m_finished, ref m_RawData, ref errorinfo);
                        break;                 
                    case ServerApduSel.DLConnectResponse:
                        iResult = DLL_Connect.Response(ref frameData, m_OIList[0], ref errorinfo);
                        break;
                    case ServerApduSel.ConnectResponse:
                        iResult = ACSE_Connect.Response(ref frameData, m_OIList[0],encryption, ref errorinfo);
                        break;
                    case ServerApduSel.DataNotification:
                        iResult = ACSE_DataNotification.Response(ref frameData, m_OIList[0], ref errorinfo);
                        break;
                    case ServerApduSel.Generalblocktransfer:
                        iResult = GeneralBlockTransfer.Response(ref frameData, ref  m_frameseq, ref m_finished, ref m_RawData);
                        break;
                    case ServerApduSel.exResponse:
                        iResult = AnlyExResponse(ref frameData,ref errorinfo);
                        break;
                    case ServerApduSel.ReleaseResponse:
                        iResult = ACSE_Release.Response(ref frameData, m_OIList[0], encryption, ref errorinfo);
                        break;
                   
                }
                if (string.IsNullOrEmpty(frameData) || iResult != 0 || !string.IsNullOrEmpty(errorinfo))
                {
                    frameData = "";
                    return iResult;
                }

                iResult = AnalizeFollowReport(ref frameData, ref errorinfo);//跟随上报信息域
                if (string.IsNullOrEmpty(frameData) || iResult != 0 || !string.IsNullOrEmpty(errorinfo))
                {
                    frameData = "";
                    return iResult;
                }             

              //  m_followreport = frameData.Substring(0, frameData.Length - 2);//跟随上报信息域              
              //  if (string.IsNullOrEmpty(m_followreport) || m_followreport == "00") return iResult;               

            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                iResult = -1;
            }
            return iResult;
        }

        private int AnlyExResponse(ref string frameData, ref string errorinfo)
        {
            try
            {
                int Emode = Convert.ToInt32(frameData.Substring(0, 2), 16);
                int ErrorFlag = Convert.ToInt32(frameData.Substring(2, 2));
                frameData = frameData.Substring(4);
                string errstr = "";
                if (Emode == 0)
                    errstr = ((StateError)ErrorFlag).ToString();
                else
                    errstr = ((ServiceError)ErrorFlag).ToString();

                errstr = clsLan.GetLan("返回异常服务标志:216 ") + clsLan.GetLan(errstr);

                for (int i = 0; i < m_OIList.Count; i++)
                    m_OIList[i].result = errstr;
            }
            catch (Exception ex) {
                errorinfo = ex.ToString();
                return -1;
            }
            return 0;
        }       

        /// <summary>
        /// 解析上报信息域，数据存贮于FROIList
        /// </summary>
        /// <param name="errorinfo"></param>
        /// <returns></returns>
        private int AnalizeFollowReport(ref string frameData,ref string errorinfo)
        {
            int iResult = 0;
            //int iExist = Convert.ToInt32(frameData.Substring(0, 2));
            //frameData = frameData.Remove(0, 2); //去掉是否有跟随域标志
            //if (iExist == 0) return iResult;

            //if (m_FROIList == null)
            //{
            //    m_FROIList = new List<CosemObject>();
            //    m_FROIList.Add(new CosemObject(new object[] { "", "跟随上报对象" }, null, null));
            //}
          
            //int smode = Convert.ToInt32(frameData.Substring(0, 2));
            //frameData = frameData.Remove(0, 2);

            //if (smode == 1)
            //{ //若干个对象属性及其数据 [1]  SEQUENCE OF A-ResultNormal
            //    iResult = GetNormalList.AnalizeData(ref frameData, m_FROIList, ref errorinfo);
            //}
            //else
            //{ //若干个记录型对象属性及其数据 [2]  SEQUENCE OF A-ResultRecord
            //    iResult = GetRecordList.AnalizeData(ref frameData, m_FROIList, ref errorinfo);
            //}
            return iResult;
        }

        /// <summary>
        /// 只解析纯数据
        /// </summary>
        /// <param name="frameData"></param>
        /// <param name="errorinfo"></param>
        /// <returns></returns>
        public int AnalizeData(string frameData, ref string errorinfo)
        {
            int iResult = 0;
            
            try
            {
                if (string.IsNullOrEmpty(frameData)) return iResult;
                if (m_OIList == null || m_OIList.Count == 0)
                {
                    m_OIList.Add(new CosemObject());
                }

                 //case ServerApduSel.DataNotification:
                 //       iResult = ACSE_DataNotification.Response(ref frameData, m_OIList[0], ref errorinfo);
                 //       break;


                iResult = Data.AnalizeData(ref frameData,m_OIList[0].DataNode,ref errorinfo);
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();
                iResult = -1;
            }
            return iResult;
        }

        /// <summary>
        /// 只组织纯数据部分
        /// </summary>
        /// <param name="errorinfo"></param>
        /// <returns></returns>
        public string GetDataFormatValue(ref string errorinfo)
        {
            string FormatValue = string.Empty;

            try
            {
                if (m_OIList == null || m_OIList.Count == 0)
                {
                    m_OIList.Add(new CosemObject());
                }
                FormatValue += Data.GetFormatValue(m_OIList[0].DataNode, ref errorinfo);
            }
            catch (Exception ex)
            {
                errorinfo = ex.ToString();                
            }
            return FormatValue;
        }

    }  


   
}
